#nullable enable

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using PHMEE.Diagnosis;
using PHMEE.Entities.Diagnosis;
using Volo.Abp.EntityFrameworkCore.Modeling;
using Volo.Abp.Users;

namespace PHMEE.EntityFrameworkCore.Extensions;

public static class DiagnosisModelCreatingExtension
{
    public static readonly JsonSerializerOptions JsonSerializerOptions = new()
    {
        WriteIndented = false
    };
    
    public static void ConfigureDiagnosis(this ModelBuilder builder)
    {
        var faultCodesStringCollectionComparer = new ValueComparer<ICollection<string>>(
            (c1, c2) => c1 == null && c2 == null || c1 != null && c2 != null && c1.SequenceEqual(c2),
            c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
            c => new Collection<string>(c.ToList())
        );
        
        builder.Entity<AlarmRecord>(e =>
        {
            e.ToTable(PHMEEConsts.DiagnosisTablePrefix + "AlarmRecords", PHMEEConsts.DbSchema);
            
            e.ConfigureByConvention();
            
            e.HasKey(x => x.Id);
            
            e.Property(x => x.VesselId).IsRequired();
            // 为 VesselId 建立索引
            e.HasIndex(x => x.VesselId);
            
            e.Property(x => x.UnitId).IsRequired();
            // 为 UnitId 建立索引
            e.HasIndex(x => x.UnitId);
            
            e.Property(x => x.EquipId);
            e.Property(x => x.PointId).IsRequired();
            e.Property(x => x.AlarmTimeUtc)
                .HasConversion(v => v,
                    v => DateTime.SpecifyKind(v, DateTimeKind.Utc))
                .IsRequired();
                
            e.Property(x => x.FaultCodes)
                .HasColumnType("json")
                .IsRequired()
                .HasConversion(
                    v => JsonSerializer.Serialize(v, JsonSerializerOptions),
                    v => JsonSerializer.Deserialize<Collection<string>>(v, JsonSerializerOptions) ?? new Collection<string>())
                .Metadata.SetValueComparer(faultCodesStringCollectionComparer);
                
            e.Property(x => x.IsNoFault).IsRequired().HasDefaultValue(false);
                
            e.Property(x => x.FeatureAlarmLevels)
                .HasColumnType("json")
                .IsRequired()
                .HasConversion(
                    v => JsonSerializer.Serialize(v, JsonSerializerOptions),
                    v => JsonSerializer.Deserialize<Dictionary<AlarmFeatureValue, AlarmLevel>>(v, JsonSerializerOptions) ?? new Dictionary<AlarmFeatureValue, AlarmLevel>())
                .Metadata.SetValueComparer(new ValueComparer<Dictionary<AlarmFeatureValue, AlarmLevel>>(
                    (c1, c2) => DictionaryEquals(c1, c2),
                    c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.Key.GetHashCode(), v.Value.GetHashCode())),
                    c => c.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)));

            e.Property(x => x.Remark).HasMaxLength(4096);
            e.Property(x => x.IsAcked).IsRequired().HasDefaultValue(false);
            // 确认者 Id，不级联删除
            e.Property(x => x.AckedById);
            // 确认者用户名
            e.Property(x => x.AckedByUserName).HasMaxLength(AbpUserConsts.MaxUserNameLength);
        });
    }
    
    private static bool DictionaryEquals(Dictionary<AlarmFeatureValue, AlarmLevel>? dict1, Dictionary<AlarmFeatureValue, AlarmLevel>? dict2)
    {
        if (dict1 == dict2)
        {
            return true;
        }

        if (dict1 == null || dict2 == null)
        {
            return false;
        }

        if (dict1.Count != dict2.Count)
        {
            return false;
        }

        foreach (var kvp in dict1)
        {
            if (!dict2.TryGetValue(kvp.Key, out var value))
            {
                return false;
            }

            if (!kvp.Value.Equals(value))
            {
                return false;
            }
        }
        return true;
    }
    
}